﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Alicanto.Lexing;

namespace Alicanto
{
    public class TokenSequence
    {
        private Queue<Token> _push;
        private Stack<Token> _insert;
        private bool _isValid = true;

        internal TokenSequence()
        {
            _push = new Queue<Token>();
            _insert = new Stack<Token>();
        }

        internal TokenSequence(params Token[] tokens) : this()
        {
            foreach (Token token in tokens)
                _push.Enqueue(token);
        }

        internal TokenSequence Update(TokenSequence value)
        {
            _push = value._push;
            _insert = value._insert;
            value._isValid = false;
            return this;
        }

        internal TokenSequence Insert(Token token)
        {
            if (!_isValid)
                throw new Exception();

            _insert.Push(token);
            return this;
        }

        internal TokenSequence Push(Token token)
        {
            if (!_isValid)
                throw new Exception();

            _push.Enqueue(token);
            return this;
        }

        internal Token Pop()
        {
            if (!_isValid)
                throw new Exception();

            if (_insert.Count > 0)
                return _insert.Pop();

            if (_push.Count > 0)
                return _push.Dequeue();

            throw new IndexOutOfRangeException();
        }

        internal Token Pop(TokenType tokenTypeExpected)
        {
            Token token = this.Pop();
            if (token.Type != tokenTypeExpected)
                throw new Exception("Unexpected token");

            return token;
        }

        internal Token Peek()
        {
            if (!_isValid)
                throw new Exception();

            if (_insert.Count > 0)
                return _insert.Peek();

            if (_push.Count > 0)
                return _push.Peek();

            return Token.Empty;
        }


        internal TokenSequence Skip(Token token)
        {
            while (this.Peek() == token)
                this.Pop();

            return this;
        }

        internal TokenSequence Skip(TokenType tokenType)
        {
            while (this.Peek().Type == tokenType)
                this.Pop();

            return this;
        }

        internal int Available
        {
            get
            {
                return _insert.Count + _push.Count;
            }
        }
    }
}
